package yatan.common.connectionpool;

import java.beans.PropertyVetoException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.log4j.Logger;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class ConnectionPool {
    private final Logger logger = Logger.getLogger(getClass());

    private static ConnectionPool instance;

    private ComboPooledDataSource dataSource;

    private ConnectionPool() {
        Properties properties = loadConfigFile("database.properties");
        String driver = readStringProperty(properties, "driver", true, null);
        String url = readStringProperty(properties, "url", true, null);
        String user = readStringProperty(properties, "user", true, null);
        String password = readStringProperty(properties, "password", true, null);

        this.dataSource = new ComboPooledDataSource();
        try {
            this.dataSource.setDriverClass(driver);
        } catch (PropertyVetoException e) {
            this.logger.error("Cannot create datasource: " + e.getMessage(), e);
        }

        this.dataSource.setJdbcUrl(url);
        this.dataSource.setUser(user);
        this.dataSource.setPassword(password);

        this.dataSource.setMaxPoolSize(10);
    }

    public static ConnectionPool getInstance() {
        if (instance == null) {
            instance = new ConnectionPool();
        }

        return instance;
    }

    public Connection getConnection() {
        try {
            Connection connection = this.dataSource.getConnection();
            connection.setAutoCommit(false);

            return connection;
        } catch (SQLException e) {
            this.logger.error("Cannot get database connection: " + e.getMessage(), e);
            return null;
        }
    }

    public void close(Connection connection) {
        try {
            connection.close();
        } catch (SQLException e) {
            logger.error("Cannot close JDBC connection: " + e.getMessage(), e);
        }
    }

    public static Properties loadConfigFile(String configFile) {
        // load configuration file
        Properties properties = new Properties();
        InputStream is = ConnectionPool.class.getClassLoader().getResourceAsStream(configFile);
        try {
            // if the file cannot be found in class path, try it from file system
            if (is == null) {
                is = new FileInputStream(configFile);
            }

            properties.load(is);
        } catch (FileNotFoundException e) {
            throw new ConnectionPoolConfigurationException("Cannot find configuration file: " + configFile, e);
        } catch (IOException e) {
            throw new ConnectionPoolConfigurationException("Cannot read configuration file: " + configFile
                    + ". IO error: " + e.getMessage());
        }

        return properties;
    }

    public static String readStringProperty(Properties properties, String key, boolean required, String defaultValue) {
        try {
            String value = (String) properties.get(key);
            if (value == null || value.trim().length() == 0) {
                if (required) {
                    throw new ConnectionPoolConfigurationException("Required property " + key
                            + " does not exist of is empty in configuration.");
                }

                return defaultValue;
            }

            return value;
        } catch (ClassCastException e) {
            throw new ConnectionPoolConfigurationException("The property " + key + " must be a string.");
        }
    }
}
